﻿using Autofac.Extras.DynamicProxy;
using Blm.VisionCloud.FrameWork.Interceptor.Log;
using Force.Crc32;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using VisionCloud.Domain.DtoModel.Sys;
using VisionCloud.Domain.DtoModel.Sys.UpdateManager;
using VisionCloud.Domain.Repository.Interface.Sys;
using VisionCloud.Utility;
using VisionCloud.Utility.Extensions;

namespace VisionCloud.Service.Sys
{
    [Intercept(typeof(LoggerInterceptor))]
    public class UpdateManagerService
    {
        public ISysUserUpdateRecordRepository SysUserUpdateRecordRepository { set; get; }

        public ISysReleaseRecordRepository SysReleaseRecordRepository { set; get; }

        public ISysPackFileUploadRepository SysPackFileUploadRepository { get; set; }

        public ILogger<UpdateManagerService> Logger { get; set; }

        /// <summary>
        /// Python环境存储目录
        /// </summary>
        private static readonly string pythonDir = ConfigHelper.GetValue<string>("PythonDev");

        /// <summary>
        /// 本地文件访问服务URL
        /// </summary>
        private static readonly string fileServer = ConfigHelper.GetValue<string>("FileServer");

        /// <summary>
        /// Surface获取是否需要升级信息
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public virtual async Task<UpdateRspDto> GetUpdateInfo(UpdateRqDto request)
        {
            var result = new UpdateRspDto { IsNeedUpdate = false };
            if (request == null)
            {
                return result;
            }

            var record = SysUserUpdateRecordRepository.GetLastRecordForUser(request.UserId);
            if (record == null || !record.CurrentVersion.IsNewVersion(request.Version))
            {
                return result;
            }
            var releaseRecord = SysReleaseRecordRepository.GetById(record.ReleaseId);
            var pfu = await SysPackFileUploadRepository.FirstOrDefaultAsync(x => x.FileKey == releaseRecord.FileKey && !x.IsDeleted);

            // LimitVersion为空：1.兼容5.2.2之前版本不区分增量和全量包 2.全量包不做版本比较
            if (pfu != null && new Version(pfu.Version) >= new Version("5.2.2"))
            {
                if (pfu.IsDifferential && new Version(request.Version) < new Version(pfu.LimitVersion))
                {
                    // 不符合最低版本
                    return result;
                }
            }

            return new UpdateRspDto
            {
                Description = releaseRecord?.Description,
                FilePath = releaseRecord.FilePath,
                IsNeedUpdate = true,
                Version = releaseRecord.Version,
                UserId = request.UserId,
                FileKey = releaseRecord.FileKey
            };
        }

        /// <summary>
        /// 获取Python模型文件和压缩包
        /// </summary>
        public async Task<DeviceUpdateResponseDto> GetPythonModes(string targetVersion, bool? isContainPt = false)
        {
            DeviceUpdateResponseDto model = new DeviceUpdateResponseDto();
            CreateDir(pythonDir);

            //python压缩包存放路径
            var devDir = Path.Combine(pythonDir, "py-env");
            CreateDir(devDir);

            //获取python压缩包
            var zipFile = Directory.GetFiles(devDir, "*.zip").FirstOrDefault();
            if (!string.IsNullOrEmpty(zipFile))
            {
                model.PythonInfo = new DeviceUpdatePythonInfo
                {
                    Version = "python36.dll",
                    FileUrl = ToWebUrl(zipFile)
                };
            }
            //获取新的加密狗安装文件
            var dongleDir = Path.Combine(pythonDir, "dongle-new");
            CreateDir(dongleDir);
            var dongleFile = Directory.GetFiles(dongleDir, "CodeMeterRuntime*.exe")?.FirstOrDefault();

            if (!string.IsNullOrEmpty(dongleFile))
            {
                model.DongleExeInfo = new DongleExeInfo
                {
                    FileName = Path.GetFileName(dongleFile),
                    //linux服务器无法使用【FileVersionInfo.GetVersionInfo(dongleFile).FileVersion】获取FileVersion
                    //TODO：先写死，后面有方法再替换
                    FileVersion = "7.51.5429.500",
                    FileUrl = ToWebUrl(dongleFile)
                };
            }
            //2023/9/6 使用新的打包逻辑
            //如果更新包已经包含pt等大文件，则不需要拉取pt等大文件
            //兼容旧的打包逻辑（isContainPt = false）
            if (isContainPt == true) return model;

            if (string.IsNullOrEmpty(targetVersion))//兼容旧版本
            {
                //模型文件存放路径
                var modeDir = Path.Combine(pythonDir, "py-model");
                //获取模型文件
                var ptFiles = Directory.GetFiles(modeDir);
                if (ptFiles != null && ptFiles.Length > 0)
                {
                    foreach (var pt in ptFiles)
                    {
                        model.ModeList.Add(new DeviceUpdateFileInfo
                        {
                            FileName = Path.GetFileName(pt),
                            CrcCode = ComputeCrc32(pt),
                            FileUrl = ToWebUrl(pt)
                        });
                    }
                }
                //获取framework部分大文件
                var framePart = Path.Combine(pythonDir, "frame-part");
                CreateDir(framePart);
                var bigDllFiles = Directory.GetFiles(framePart, "*.dll");
                if (bigDllFiles?.Length > 0)
                {
                    foreach (var file in bigDllFiles)
                    {
                        model.FrameBigParts.Add(new DeviceUpdateFileInfo
                        {
                            FileName = Path.GetFileName(file),
                            CrcCode = ComputeCrc32(file),
                            FileUrl = ToWebUrl(file)
                        });
                    }
                }
            }
            else
            {
                var targetVersionDir = Path.Combine(pythonDir, targetVersion);
                CreateDir(targetVersionDir);
                foreach (var file in Directory.GetFiles(targetVersionDir))
                {
                    model.FrameBigParts.Add(new DeviceUpdateFileInfo
                    {
                        FileName = Path.GetFileName(file),
                        CrcCode = ComputeCrc32(file),
                        FileUrl = ToWebUrl(file)
                    });
                }
            }

            return model;
        }

        /// <summary>
        /// 创建文件夹
        /// </summary>
        /// <param name="dir"></param>
        private static void CreateDir(string dir)
        {
            if (!Directory.Exists(dir))
                Directory.CreateDirectory(dir);
        }

        /// <summary>
        /// 计算文件crc
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static string ComputeCrc32(string filename)
        {
            var crc = 0u;
            using (var f = File.OpenRead(filename))
            {
                var buffer = new byte[65536];
                while (true)
                {
                    var count = f.Read(buffer, 0, buffer.Length);
                    if (count == 0)
                        break;
                    crc = Crc32CAlgorithm.Append(crc, buffer, 0, count);
                }
            }
            return crc.ToString("X8");
        }

        /// <summary>
        /// 将本地文件转化为可访问URL
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        private string ToWebUrl(string filePath)
        {
            var arr = filePath.Split("device-update");
            var path = arr.Length == 2 ? arr[1] : arr[0];
            var line = path.StartsWith("/") ? "" : "/";
            return $"{fileServer}/deviceupdate{line}{path}";
        }
    }
}